package com.web.portal.services.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.web.portal.mapper.UserMapper;
import com.web.portal.pojo.PortalUser;
import com.web.portal.services.UserService;
import com.web.portal.utils.*;
import com.wf.captcha.ArithmeticCaptcha;
import com.wf.captcha.GifCaptcha;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Random;

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, PortalUser>
        implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Qualifier("redisUtil")
    @Autowired
    private RedisUtil redisUtils;

    @Autowired
    private Random random;

    public static final int[] captcha_font_types = {Captcha.FONT_1,
            Captcha.FONT_2, Captcha.FONT_3, Captcha.FONT_4,
            Captcha.FONT_5, Captcha.FONT_6, Captcha.FONT_7,
            Captcha.FONT_8, Captcha.FONT_9, Captcha.FONT_10};

    @Override
    public PortalUser selectByUserCode(String userCode) {
        QueryWrapper<PortalUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_code", userCode);
        return userMapper.selectOne(queryWrapper);
    }

    /**
     * 获取用户信息
     *
     * @param userCode
     * @return
     */
    @Override
    public PortalUser selectByUserCodeInfo(String userCode) {
        QueryWrapper<PortalUser> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .select("id", "user_code", "name", "user_name", "roles", "avatar", "mobile", "sign"
                        , "state", "reg_ip", "login_ip", "create_time", "update_time"
                        , "start_year", "sex", "major")
                .eq("user_code", userCode);
        PortalUser portalUser = userMapper.selectOne(queryWrapper);
        convertUser(portalUser);
        return portalUser;
    }

    /**
     * 验证码
     *
     * @param response
     * @param captchaKey
     * @throws Exception
     */
    @Override
    public void createCaptcha(HttpServletResponse response, String captchaKey) throws Exception {
        if (TextUtils.isEmpty(captchaKey) || captchaKey.length() < 13) {
            return;
        }
        long key = 0;
        try {
            key = Long.parseLong(captchaKey);
        } catch (Exception e) {
            return;
        }
        // 设置请求头为输出图片类型
        response.setContentType("image/gif");
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        int captchaType = random.nextInt(3);
        Captcha targetCaptcha = null;
        if (captchaType == 0) {
            // 三个参数分别为宽、高、位数
            targetCaptcha = new SpecCaptcha(200, 60, 5);
        } else if (captchaType == 1) {
            // git 类型
            targetCaptcha = new GifCaptcha(200, 60);
        } else {
            // 算术类型
            targetCaptcha = new ArithmeticCaptcha(200, 60);
            targetCaptcha.setLen(2);
            targetCaptcha.text();
        }

        int index = random.nextInt(captcha_font_types.length);
        log.info("captcha font type index == > " + index);
        targetCaptcha.setFont(captcha_font_types[index]);
        //  数字和字母混合
        targetCaptcha.setCharType(Captcha.TYPE_DEFAULT);
        String content = targetCaptcha.text().toLowerCase();
        log.info("captcha content == >" + content);
        // 保存到 redis
        redisUtils.set(Constants.User.KEY_CAPTCHA_CONTENT + key, content, 60 * 10);
        targetCaptcha.out(response.getOutputStream());
    }

    @Override
    public PortalUser checkSobUser() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        HttpServletResponse response = requestAttributes.getResponse();
        // 拿到token_key
        String tokenKey = CookieUtils.getCookie(request, Constants.User.COOKIE_TOKE_KEY);
        log.info("checkSobUser tokenKey => " + tokenKey);
        PortalUser portalUser = paresByTokenKey(tokenKey);
        log.info("checkSobUser tokenKey => " + portalUser);
        if (portalUser == null) {
            // 说明解析出错了,过期了
            try {
                // 5、如果refreshToken有效，创建新的token，和新的refreshToken
                // 千万别这么干，事务还没有提交，如果这样设置，数据库里的密码就没有了
                // userFromDb.setPassword("");
                // 删除 refreshToken 的记录
                PortalUser getUser = userMapper.selectById(portalUser.getId());
                if (getUser == null) {
                    log.info("refresh token is null");
                    return null;
                }
                String newTokenKey = createToken(response, portalUser);
                // 返回token
                log.info("created new token and refresh token...");
                log.info("newTokenKey ==> " + newTokenKey);
                return paresByTokenKey(newTokenKey);
            } catch (Exception ex) {
                log.info("refresh token is 过期了...");
                // 4、如果refreshToken过期了，就当前访问没有登录，提示用户登录
                return null;
            }
        }
        return portalUser;
    }

    @Override
    public String createToken(HttpServletResponse response, PortalUser portalUserFromDb) {
        // 生成token
        Map<String, Object> claims = ClaimsUtils.sobUser2Claims(portalUserFromDb);
        // token默认有效为2个小时
        String token = JwtUtil.createToken(claims);
        // 返回token的md5值，token 会保存到redis里
        // 前端访问的时候，携带token的md5key,从redis 中获取即可
        String tokenKey = DigestUtils.md5DigestAsHex(token.getBytes());
        redisUtils.set(Constants.User.KET_TOKEN + tokenKey, token, 60 * 60 * 2);
        // 保存token到redis里，有效期为2个小时，key 是tokenKey
        // 把tokenKey写到cookies里
        CookieUtils.setUpCookie(response, Constants.User.COOKIE_TOKE_KEY, tokenKey);

        // 生成 refreshTokenValue
        JwtUtil.createRefreshToken(portalUserFromDb.getId(),
                Constants.TimeValueInMillions.MONTH);
        return token;
    }

    public PortalUser paresByTokenKey(String tokeKey) {
        String token = (String) redisUtils.get(Constants.User.KET_TOKEN + tokeKey);
        log.info("paresByTokenKey token ==> " + token);
        if (tokeKey != null) {
            try {
                log.info("token not's null");
                Claims claims = JwtUtil.parseJWT(token);
                return ClaimsUtils.claims2Claims(claims);
            } catch (Exception e) {
                log.info("paresByTokenKey Exception");
                return null;
            }
        }
        log.info("paresByTokenKey null");
        return null;
    }

    @Override
    public boolean isAvailable(PortalUser portalUser) {
        PortalUser checkSobUser = checkSobUser();
        // 被修改为成员 修改者为管理员
        if ((checkSobUser.getRoles().equals("1") || checkSobUser.getRoles().equals("2") &&
                portalUser.getRoles().equals("0"))
                || portalUser.getUserCode().equals(checkSobUser.getUserCode())
                // 超级管理员修改管理员
                || (checkSobUser.getRoles().equals("0") && portalUser.getRoles().equals("1"))) { // 判断是否有足够的权限
            return true;
        }
        return false;
    }

    public void convertUser(PortalUser portalUser) {
        portalUser.setRoles(getRole(portalUser.getRoles()));
        portalUser.setState(getState(portalUser.getState()));
    }

    public String getRole(String role) {
        switch (role) {
            case "0":
                return "超级管理员";
            case "1":
                return "管理员";
            case "2":
                return "成员";
            default:
                return role;
        }
    }

    public String getState(String state) {
        switch (state) {
            case "0":
                return "开除";
            case "1":
                return "在校";
            case "2":
                return "离校";
            default:
                return state;
        }
    }
}
